package com.choupin.choupin.utils.redis;

import cn.hutool.json.JSONUtil;
import com.choupin.choupin.utils.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@SuppressWarnings("ALL")
@Component
public class RedisUtils {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private   PLock pLock;


    /*
     这个方法addRedis(...)是业务中用来首次建立缓存的
     */
//     添加redis键值对
    public  void  addRedis(String  key,String value){
//        创建一个valueOperations该对象用于操作redis数据
        ValueOperations valueOperations=stringRedisTemplate.opsForValue();
        try {
            String redisValue=objectMapper.writeValueAsString(value);
//            将键值对写入redis中
            valueOperations.set(key,redisValue);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

 /*
    查看缓存
    这个方法selectRedis(...)是业务用来查看缓存数据，以减少对数据库的访问压力
  */
    public  Object selectRedis(String key,String lockkey,Object value){
//   从redis商铺中查找数据
        String redisJson=stringRedisTemplate.opsForValue().get(key);
//     判断是否命中（数据存在缓存中）缓存，不为空说明命中缓存直接返回结果，否则下一步
        if (StrUtil.isNotBlank(redisJson)){
            Object resultRedis= JSONUtil.toBean(redisJson,Object.class);
            return resultRedis;
        }
//        因为isNotBlank这个判断完不为真（即没空）的时候，又有两种情况，
//        一：为空字符串（数据库中没有此数据（缓存穿透），用空字符串方式解决）
//        二:为null说明没有建立该缓存，下一步重建缓存
        //redis为空字符串，则缓存穿透（访问的数据缓存中不存在，数据库中也不存在）
        if (redisJson!=null){
            return null;
        }
        //实现缓存重建
        Object resultRedis=null;

        //解决缓存击穿，锁互斥
        try {
            //获取锁
            boolean isLock=pLock.tryLock(lockkey);
            //判断是否获取锁成功！
            if (!isLock){
                //否则，失败休眠并重试
                Thread.sleep(50);
                return value;
            }

            //二次检查缓存是否穿透问题
            redisJson=stringRedisTemplate.opsForValue().get(key);

            //二次查询命中，如果命中缓存，直接返回
            if(StrUtil.isNotBlank(redisJson)){
                resultRedis=JSONUtil.toBean(redisJson,Object.class);
                return resultRedis;
            }

            //继续判断是否为空字符串或null
            if (redisJson!=null){
                return null;
            }

            //查询数据库，重写缓存
            resultRedis=value;

            //判断查询的值是否为null，为null为了防止缓存穿透，给缓存赋予空字符串
            if (resultRedis==null){
                stringRedisTemplate.opsForValue().set(key,"",3, TimeUnit.SECONDS);
                return null;
            }
            //写入缓存
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(resultRedis),30,TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放互斥锁
            pLock.tryLock(lockkey);
        }
        return resultRedis;
    }



/*
   用来代替session做会话使用，具体是登录的会话，用于存储登录信息和登录失效设置
 */
    public static void RedisLoginSession(String phone){

//         链接redis服务器
        Jedis jedis=new Jedis("localhost",6379);
//        获取redis键值对的信息
         String lpone=jedis.get("phone:"+phone);
         String logintimestr=jedis.get("time:"+phone);
        System.out.println("时间为："+logintimestr);
        System.out.println("截取的时间为："+logintimestr.substring(24,25));
//        截取主要部分

//        设置日期转换格式
        DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//         并将其转换回LocalDateTime对象
        LocalDateTime logintime=LocalDateTime.parse(logintimestr.substring(1,20),dateTimeFormatter);
//        获取当前时间
        LocalDateTime now=LocalDateTime.now();
        // 计算now和logintime的差值
        long sumtime = ChronoUnit.SECONDS.between(logintime, now);
//        并将其转回整型多少秒
        int intSumtime = (int) Math.abs(sumtime);
//        转换成多少天
        int day=intSumtime/(60*60*24);
        //        判断最后一次登录距离现在是否超过一个月，超过则登录失效
      LoginFailure(day,30,phone);
//  关闭redis数据库
        jedis.close();
    }


//    设置redis登录失效时间
    /*   time 表示上一次登录距离这次登录的时间差
    *    outtime 表示保存多少天内的登录信息
    *    phone 表示缓存登录信息的redis的唯一键名
    * */
    public static Boolean  LoginFailure(long time,long outtime,String phone){
        //         链接redis服务器
        Jedis jedis=new Jedis("localhost",6379);
        if(time>outtime){
//            登陆失效，将redis键值对删除
            jedis.del("phone:"+phone);
            jedis.del("time:"+phone);
            jedis.del("password:"+phone);
            return true;
        }
        return false;
    }


//通过key获取redis的值
    public  static String getValue(String key){
        //         链接redis服务器
        Jedis jedis=new Jedis("localhost",6379);
      //获取key的value值
        String value=jedis.get(key);
      // 由于从redis中取出的字符串是带有""的所以要去除
        value=value.substring(1,value.length()-1);
        //关闭链接
        jedis.close();
        return value;
    }

//    普通方式获取字符串
public  static String getCommonValue(String key){
    //         链接redis服务器
    Jedis jedis=new Jedis("localhost",6379);
    //获取key的value值
    String value=jedis.get(key);
    //关闭链接
    jedis.close();
    return value;
}

//    删除键值对
    public static void deleteKeyValue(String key){
        //         链接redis服务器
        Jedis jedis=new Jedis("localhost",6379);
        //删除键值对
        jedis.del(key);
        //关闭链接
        jedis.close();
    }

    /**
     * 判断Redis的String缓存是否存在
     * @param key String缓存的key
     * @return 如果存在返回true，否则返回false
     */
    public static boolean isExist(String key) {
        //         链接redis服务器
        Jedis jedis=new Jedis("localhost",6379);
        Boolean isExists=jedis.exists(key);
        //关闭链接
        jedis.close();
            return isExists;
    }


}
